import pandas as pd
import numpy as np
import dalex as dx
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
import warnings
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.pipeline import Pipeline
warnings.filterwarnings('ignore')
pd.options.display.max_rows = 4000
df = pd.read_csv('heart_data.csv')
y = df.target.values
x = df.drop(['target'], axis = 1)
x_train, x_test, y_train, y_test = train_test_split(x,y, test_size = 0.2,random_state=0, stratify=y)
ss = StandardScaler()
p_svc = Pipeline(
[
('standardscaler', ss),
('SVC', SVC( C = 0.5, random_state=1, probability=True))
]
)
p_svc.fit(x_train, y_train)
Pipeline(steps=[('standardscaler', StandardScaler()),
('SVC', SVC(C=0.5, probability=True, random_state=1))])
expl = dx.Explainer(p_svc, x_train, y_train, label='SVC')
Preparation of a new explainer is initiated -> data : 242 rows 20 cols -> target variable : 242 values -> model_class : sklearn.svm._classes.SVC (default) -> label : SVC -> predict function : <function yhat_proba_default at 0x0000021500D7CA60> will be used (default) -> predict function : Accepts pandas.DataFrame and numpy.ndarray. -> predicted values : min = 0.0525, mean = 0.446, max = 0.982 -> model type : classification will be used (default) -> residual function : difference between y and yhat (default) -> residuals : min = -0.934, mean = 0.0127, max = 0.915 -> model_info : package sklearn A new explainer has been created!
Mój zbiór danych przedstawia osoby podejrzane o występowanie choroby wieńcowej. Zmienne zawierają wyniki różnych badań, poszczególne z nich zostaną omówione poniżej.
Będę korzystał z modelu SVC ponieważ nasze analizy pokazały, że jest on najlepszy do naszego problemu
df.head()
| age | sex | trestbps | chol | fbs | restecg | thalach | exang | oldpeak | ca | ... | thal_fd | thal_rd | slope_up | slope_flat | slope_down | cp_ta | cp_aa | cp_np | cp_a | target | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 63 | 1 | 145 | 233 | 1 | 1 | 150 | 0 | 2.3 | 0 | ... | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 |
| 1 | 67 | 1 | 160 | 286 | 0 | 1 | 108 | 1 | 1.5 | 3 | ... | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 |
| 2 | 67 | 1 | 120 | 229 | 0 | 1 | 129 | 1 | 2.6 | 2 | ... | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 |
| 3 | 37 | 1 | 130 | 250 | 0 | 0 | 187 | 0 | 3.5 | 0 | ... | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 |
| 4 | 41 | 0 | 130 | 204 | 0 | 1 | 172 | 0 | 1.4 | 0 | ... | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
5 rows × 21 columns
Wybieramy pierwszą obserwację ze zbioru testowego. Jest to 68 letni mężczyzna z wysokim choresterolem, bólem niedławicowym, poziomem cukru powyżej 120, odwracalną wadą serca.
Ma on chorobę wieńcową. Nasz model również zaklasyfikował go jako chorego.
obs = x_test.iloc[1:2]
y_obs = y_test[1]
obs
| age | sex | trestbps | chol | fbs | restecg | thalach | exang | oldpeak | ca | thal_n | thal_fd | thal_rd | slope_up | slope_flat | slope_down | cp_ta | cp_aa | cp_np | cp_a | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 83 | 68 | 1 | 180 | 274 | 1 | 1 | 150 | 1 | 1.6 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 |
print('Rzeczywista wartość targetu: ' + str(y_obs))
print('Przewidywana wartość targetu: ' + str(p_svc.predict(obs)))
Rzeczywista wartość targetu: 1 Przewidywana wartość targetu: [1]
Nasze dane składają się głownie ze zmiennych binarnych przez co metoda CP nie jest najbardziej optymalną, ale mimo to może dać ciekawe wyniki, takie jak:
pd.options.display.max_rows = 4000
cp2 = expl.predict_profile(obs)
cp2.plot()
Calculating ceteris paribus: 100%|████████████████████████████████████████████████████| 20/20 [00:00<00:00, 181.32it/s]
Wziąłem dwie obserwację o różnych wartościach targetu. Dzięki temu możemy zauważyć pewien trend pojawiający się w większości predykcji.
Kiedy pacjent jest zdrowy to wzrost cholesterolu wpływa na zwiększenie predykcji, a u osoby chorej zmniejsza. Tak jak w zadaniu 2, może to być spowodwane obserwacją lekarską osób z dużym cholesterolem.
Podobny wniosek można wyciągnąć względem zmiennej cp_a = 1 której występowanie u osoby chorej jest dobrym obiawem ponieważ oznacza brak inny, poważniejszych rodzajów bólu.
x_test.iloc[11:13]
| age | sex | trestbps | chol | fbs | restecg | thalach | exang | oldpeak | ca | thal_n | thal_fd | thal_rd | slope_up | slope_flat | slope_down | cp_ta | cp_aa | cp_np | cp_a | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 157 | 58 | 1 | 125 | 300 | 0 | 1 | 171 | 0 | 0.0 | 2 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 0 |
| 148 | 45 | 1 | 128 | 308 | 0 | 1 | 170 | 0 | 0.0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 |
cp3 = expl.predict_profile(x_test.iloc[11:13])
cp3.plot()
Calculating ceteris paribus: 100%|████████████████████████████████████████████████████| 20/20 [00:00<00:00, 122.09it/s]
Uważam, że narzędzie CP nie jest dobrą metodą do badania obserwacji z naszego zbioru, ponieważ większość istotnych zmiennych w naszym modelu jest kategoryczna, a w tej metodzie chodzi o obserwację predykcji w zależności od niewielich wachcań zmiennych. Mimo to dzięki możemy zaobserwować dynamikę predykcji według zmiennych ciągłych, warte odnotowania jest to że wiek pacjentów ma marginalny wpływ na wynik modelu oraz to jak wpływ zmiennej chol jest zależny od wartości targetu.